package jp.bluecab.bluecabsystem.locrecserv.locrec;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.bluecab.bluecabsystem.common.CommonFunc;
import jp.bluecab.bluecabsystem.common.PMF;
import jp.bluecab.bluecabsystem.locrecserv.classes.GPSLatest;
import jp.bluecab.bluecabsystem.locrecserv.classes.GPSRecord;
import jp.bluecab.bluecabsystem.locrecserv.classes.LocRecUser;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

@SuppressWarnings("serial")
public class APIServlet extends HttpServlet {
	
	private static final Logger log = Logger.getLogger(APIServlet.class.getName());
	
	private static final int SAVE_TERM = 1;// week
	private int post_count = 0;
	
	/**
	 * postメソッドを受信した場合
	 */
	@SuppressWarnings("unchecked")
	public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		String mode = "new";
		long loginid = 0;
		float latitude = -1; // 緯度
		float longitude = -1; // 経度
		float orientation = -1; // 経度
		int op_mode = -1;
		
		try {
			String param = req.getParameter("mode");
			if (param != null) {
				mode = param;
			}
			loginid = Long.parseLong(req.getParameter("loginid"));
		} catch (IllegalArgumentException ex) {
			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		if (mode.equals("new")) {
			// 位置情報をdatastoreに新規追加
			try {
				latitude = Float.parseFloat(req.getParameter("latitude"));
				longitude = Float.parseFloat(req.getParameter("longitude"));
				orientation = Float.parseFloat(req.getParameter("orientation"));
				op_mode = Integer.parseInt(req.getParameter("op_mode"));
				
				if ((latitude < -90) || (90 < latitude)) {
					throw new NumberFormatException();
				}
				
				if ((longitude < -180) || (180 < longitude)) {
					throw new NumberFormatException();
				}
				
			} catch (NumberFormatException ex) {
				resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
				return;
			}
			
			Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"));
			Date nowdatetime = cal.getTime();
			GPSRecord rec = new GPSRecord(loginid, latitude, longitude, orientation, op_mode, nowdatetime);
			try {
				pm.makePersistent(rec);
			} finally {
				pm.close();
			}
			
			// Latestレコードに登録
			pm = PMF.get().getPersistenceManager();
			
			Query query = pm.newQuery(GPSLatest.class);
			query.setFilter("userid==puserid");
			query.declareParameters("String puserid");
			
			List<GPSLatest> results = (List<GPSLatest>) query.execute(loginid);
			if (results != null && results.size() != 0) {// 最初の一回
				pm.deletePersistentAll(results);
			}
			
			GPSLatest gpsLatest = new GPSLatest(loginid, latitude, longitude, orientation, op_mode, nowdatetime);
			try {
				pm.makePersistent(gpsLatest);
				post_count++;
			} finally {
				pm.close();
			}
			
			// 一定量を削除
			if (post_count >= 200) {
				deletePastData();
				post_count = 0;
			}
		}
		
		if (mode.equals("delete")) {
			// 位置情報を削除する
			Query query = pm.newQuery(GPSRecord.class);
			query.setFilter("loginid==pLoginId");
			query.declareParameters("String pLoginId");
			
			List<GPSRecord> results = (List<GPSRecord>) query.execute(loginid);
			if (results != null && results.size() != 0) {
				pm.deletePersistentAll(results);
			}
		}
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		String result = "";
		
		try {
			int target_no = 0;
			String target = req.getParameter("target");
			if (target != null) {
				target_no = Integer.parseInt(target);
			}
			
			switch (target_no) {
			case 0:
				result = getLatestGPSInfo();
				break;
			
			case 1:
				result = getHistoryGPSCount(req);
				break;
			case 2:
				result = getHistoryGPSInfo(req);
				break;
			}
			
		} catch (IllegalArgumentException ex) {
			resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		
		resp.setContentType("application/json");
		resp.getWriter().println(result);
	}
	
	@SuppressWarnings("unchecked")
	private String getLatestGPSInfo() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Query pmquery = pm.newQuery(GPSLatest.class);
		
		List<GPSLatest> results = (List<GPSLatest>) pmquery.execute();
		
		if (results.size() == 0) {
			return "";
		}
		
		// jsonデータを生成する
		GsonBuilder gsonBuilder = new GsonBuilder();
		gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
		Gson gson = gsonBuilder.create();
		
		return gson.toJson(results);
	}
	
	private class QueryParam {
		String tz;
		String loginid;
		long taxino;
		Date beginDateTime;
		Date endDateTime;
		int pageno;
		
		public void getParamFromReq(HttpServletRequest req) {
			this.pageno = 1;// ページ番号
			this.loginid = "";
			this.taxino = 0;
			
			// 最初から取得できる時刻を設定しておく
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.HOUR, cal.get(Calendar.HOUR) - 24);
			this.beginDateTime = cal.getTime();
			
			// 現在の時刻を入れておく
			this.endDateTime = new Date();
			
			String str = req.getParameter("pageno");
			if (str != null) {
				this.pageno = Integer.parseInt(str);
			}
			
			if (this.pageno < 1) {
				this.pageno = 1;
			}
			
			UserService userService = UserServiceFactory.getUserService();
			User user = userService.getCurrentUser();
			
			this.loginid = req.getParameter("loginid");
			if (this.loginid == null) {
				this.loginid = findloginid(user.getEmail());
			}
			if (this.loginid == null) {// ユーザー特定不可
				throw new IllegalArgumentException();
			}
			
			tz = req.getParameter("tz");// クライアントのタイムゾーン取得
			
			this.taxino = CommonFunc.getLongDef(req.getParameter("taxino"), 0);
			str = req.getParameter("datebegin");
			if (str != null) {
				this.beginDateTime = parseDatetime(str, tz);
			}
			
			str = req.getParameter("dateend");
			if (str != null) {
				this.endDateTime = parseDatetime(str, tz);
			}
		}
	}
	
	@SuppressWarnings({ "rawtypes" })
	private String getHistoryGPSCount(HttpServletRequest req) {
		QueryParam param = new QueryParam();
		param.getParamFromReq(req);
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		StringBuffer query = new StringBuffer();
		query.append(" loginid == pLoginId");
		query.append(" && createdAt > pStartDateTime");
		query.append(" && createdAt < pEndDateTime");
		
		Query pmquery = pm.newQuery(GPSRecord.class);
		pmquery.setFilter(query.toString());
		pmquery.declareParameters("long pLoginId, java.util.Date pStartDateTime, java.util.Date pEndDateTime");
		int count = ((List) pmquery.execute(param.taxino, param.beginDateTime, param.endDateTime)).size();
		
		return String.valueOf(count);
		
	}
	
	@SuppressWarnings("unchecked")
	private String getHistoryGPSInfo(HttpServletRequest req) {
		QueryParam param = new QueryParam();
		param.getParamFromReq(req);
		
		int range = 20;// 1ページあたりの件数
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		StringBuffer query = new StringBuffer();
		query.append(" loginid == pLoginId");
		query.append(" && createdAt > pStartDateTime");
		query.append(" && createdAt < pEndDateTime");
		
		Query pmquery = pm.newQuery(GPSRecord.class);
		pmquery.setFilter(query.toString());
		
		// 1回の検索で最大20件返す
		int startrange = range * (param.pageno - 1);
		int endrange = startrange + range;
		pmquery.setRange(startrange, endrange);
		
		pmquery.declareParameters("long pLoginId, java.util.Date pStartDateTime, java.util.Date pEndDateTime");
		
		List<GPSRecord> results = (List<GPSRecord>) pmquery.execute(param.taxino, param.beginDateTime, param.endDateTime);
		
		// jsonデータを生成する
		GsonBuilder gsonBuilder = new GsonBuilder();
		gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
		Gson gson = gsonBuilder.create();
		
		// Gson gson = new Gson();
		return gson.toJson(results);
	}
	
	private void deletePastData() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		StringBuffer query = new StringBuffer();
		query.append(" createdAt < pEndDateTime");
		
		Query pmquery = pm.newQuery(GPSRecord.class);
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.DATE, -7 * SAVE_TERM);
		Date endDateTime = cal.getTime();
		
		pmquery.setFilter(query.toString());
		pmquery.declareParameters("java.util.Date pEndDateTime");
		
		@SuppressWarnings("unchecked")
		List<GPSRecord> results = (List<GPSRecord>) pmquery.execute(endDateTime);
		if (results.size() > 0) {
			pm.deletePersistentAll(results);
		}
		pm.close();
	}
	
	@SuppressWarnings("unchecked")
	private String findloginid(String pEmail) {
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		Query pmquery = pm.newQuery(LocRecUser.class);
		pmquery.setFilter("email == pEmail");
		pmquery.declareParameters("String pEMail");
		
		List<LocRecUser> results = (List<LocRecUser>) pmquery.execute(pEmail);
		
		if (results.size() == 0) {
			return null;
		}
		return results.get(0).getLoginId();
	}
	
	/**
	 * 日時の文字列をDate型に変換する
	 */
	private Date parseDatetime(String dateTime, String tz) {
		
		Date parsed = null;
		try {
			String timezone = "UTC";
			if (tz != null) {
				timezone = tz;
			}
			
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/M/d-kk:mm");
			sdf.setTimeZone(TimeZone.getTimeZone(timezone));
			parsed = sdf.parse(dateTime);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return parsed;
	}
}
